#include "server.h"
#include "recipe.h"
#include "group.h"

using std::ostream;

/************ DO NOT CHANGE ************/
namespace mtm {
shared_ptr<Server> Server::instance(new Server());
/************ DO NOT CHANGE ************/

shared_ptr<User>& Server::getUser(unsigned id) {
	for (auto& pqe : users) {
		if (id == pqe.priority) {
			return pqe.data;
		}
	}
	throw UserNotConnectedException();
}

shared_ptr<Group>& Server::getGroup(const string& name) {
	for (auto& group : groups) {
		if (group->getName() == name) {
			return group;
		}
	}
	throw NoSuchGroupException();
}

void Server::connect(shared_ptr<User>& user) {
	if (!user) {
		throw IllegalArguments();
	}
	try {
		users.insert(user->getId(), user);
	} catch (ElementAlreadyExists) {
		throw UserAlreadyConnectedException();
	}
}

void Server::disconnect(shared_ptr<User>& user) {
	if (!user) {
		throw IllegalArguments();
	}
	user=getUser(user->getId()); //maybe we have leader
	users.erase(user);
}

ostream& Server::printUsersList(ostream& os) {
	return os << users;
}

ostream& Server::printMostActiveGroups(ostream& os) {
	PriorityQueue<int, shared_ptr<Group>> pq;
	for (auto group : groups) {
		pq.insert(group->activityLevel(), group);
	}
	return os << pq;
}

void Server::cleanMemory(int k) {
	if (k < 0) {
		throw IllegalArguments();
	}
	for (auto group : groups) {
		group->cleanMemory(k);
	}
}

shared_ptr<PowerUser> Server::openNewGroup(unsigned userId,
		const string& groupName) {
	auto& user = getUser(userId);
	if (user->getGroup()) {
		throw AlreadyMemberInGroupException();
	}
	try {
		getGroup(groupName);
		throw GroupAlreadyExistsException();
	} catch (NoSuchGroupException) {
	}
	shared_ptr < PowerUser > leader = makeLeader(user);
	shared_ptr < Group > group(new Group(groupName, leader));
	groups.push_back(group);
	leader->joinRequestCallback(true, group);
	return leader;
}

void Server::requestToJoinGroup(unsigned userId, const string& groupName) {
	auto user = getUser(userId);
	if (user->getGroup()) {
		throw AlreadyMemberInGroupException();
	}
	auto group = getGroup(groupName);
	group->joinRequest(user);
}

void Server::requestToLeaveGroup(unsigned userId, const string& groupName) {
	auto user = getUser(userId);
	if (!user->getGroup()) {
		throw NotInGroupException();
	}
	auto group = getGroup(groupName);
	group->leaveRequest(user);
}

void Server::validateUserGroup(unsigned userId) {
	if (!getUser(userId)->getGroup()) {
		throw NotInGroupException();
	}
}

shared_ptr<PowerUser> Server::makeLeader(const shared_ptr<User>& newLeader) {
	shared_ptr < PowerUser > leader(new PowerUser(*newLeader));
	for (auto& user : users) {
		if (user.data == newLeader) {
			user.data = leader;
		}
	}
	return leader;
}

}
